package com.zbest.zpay.acctrans.component;

import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.zbest.common.lang.base.Money;
import com.zbest.common.lang.util.CoreDateUtils;
import com.zbest.zpay.acctrans.common.exception.AccTransRuntimeException;
import com.zbest.zpay.acctrans.dal.mapper.AccountBalanceMapper;
import com.zbest.zpay.acctrans.dal.mapper.AccountDetailMapper;
import com.zbest.zpay.acctrans.dal.mapper.AccountHoldMapper;
import com.zbest.zpay.acctrans.dal.model.AccountBalance;
import com.zbest.zpay.acctrans.dal.model.AccountDetail;
import com.zbest.zpay.acctrans.dal.model.AccountHold;
import com.zbest.zpay.acctrans.dal.model.AccountInf;
import com.zbest.zpay.acctrans.dal.model.TransJnl;
import com.zbest.zpay.acctrans.enums.CapStsFlg;
import com.zbest.zpay.acctrans.enums.DebitCreditHoldFlag;
import com.zbest.zpay.acctrans.enums.HoldStatus;
import com.zbest.zpay.acctrans.enums.UpdBalFlg;
import com.zbest.zpay.acctrans.enums.VchNoType;
import com.zbest.zpay.acctrans.enums.biz.AcctransBizCode;

public class MainAccountHoldComponent {

    private static final Logger logger = LoggerFactory
            .getLogger(MainAccountHoldComponent.class);

    private Money zero = new Money(0, Money.DEFAULT_CURRENCY);

    @Autowired
    private AccountBalanceMapper accountBalanceMapper;

    @Autowired
    private AccountDetailMapper accountDetailMapper;

    @Autowired
    private AccountHoldMapper accountHoldMapper;

    public void execute(TransJnl transJnl, AccountInf accountInf) {
        logger.info("[MAIN ACCOUNT HOLD!]");
        if (transJnl == null) {
            logger.info("AccountTransJnl不能为空");
            throw new AccTransRuntimeException(AcctransBizCode.ACT_FIELD_NOT_NULL, "transJnl不能为空");
        }
        String actNo = accountInf.getActNo();
        logger.info("[查询主账户余额信息]");
        AccountBalance mainAccountBalance = null;

        /** 在主账户余额信息表中判断是否存在该账户的余额信息,如果存在再 for update该记录，避免死锁 */
        long retNum = accountBalanceMapper.qryCntByActNo(actNo);
        if (retNum == 1) {
            mainAccountBalance = accountBalanceMapper.qryByActNoForUpd(actNo);
        }

        if (mainAccountBalance == null) {
            throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_BAL_NOT_EXIST,
                    "主账户余额信息不存在");
        }

        updateHoldTable(transJnl, mainAccountBalance);
        updateAccountBalance(transJnl, mainAccountBalance);
        writeMainActDrCrDetail(transJnl, mainAccountBalance);
    }

    public void updateHoldTable(TransJnl accountTransJnl,
                                AccountBalance mainAccountBalance) {
        logger.info("[UPD HOLD TABLE!]");
        Money hldAmt;
        Money hldBal;
        String hldNo = accountTransJnl.getHldNo();

        logger.info("[查询主账户冻结解冻信息]");
        AccountHold mainAccountHold = null;
        long retNum = accountHoldMapper.queryCntByHldNo(hldNo);
        if (retNum == 1) {
            mainAccountHold = accountHoldMapper.queryHoldInfForUpd(hldNo);
            // 检查同冻结编号下追加的冻结账户是否一致
            if (!StringUtils.equals(mainAccountHold.getUsrId(), accountTransJnl.getHldUsrId())) {
                throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_HOLD_ACCOUNTDIFF,
                        "同冻结编号下追加的冻结账户必须一致");
            }
            hldAmt = new Money(accountTransJnl.getHldAmt(), Money.DEFAULT_CURRENCY);
            hldBal = new Money(mainAccountHold.getHldBal(), Money.DEFAULT_CURRENCY).add(hldAmt);
            int retCd = accountHoldMapper.updateBalByHldNo(HoldStatus.HOLD_HOLD.getCode(), hldBal.getCent(), hldNo);
            if (retCd == 0) {
                throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_NOT_EXIST_HOLD,
                        "不存在该冻结编号信息");
            }
        } else if (retNum == 0) {
            hldAmt = new Money(accountTransJnl.getHldAmt(), Money.DEFAULT_CURRENCY);
            AccountHold mainAccountHoldStore = new AccountHold();
            mainAccountHoldStore.setActNo(mainAccountBalance.getActNo());
            mainAccountHoldStore.setCcy(accountTransJnl.getCcy());
            mainAccountHoldStore.setDueProcMod(accountTransJnl.getHldExpProcMod());
            mainAccountHoldStore.setEffDat(accountTransJnl.getActDat());
            mainAccountHoldStore.setEffTim(accountTransJnl.getSysTim());
            mainAccountHoldStore.setExpDat(accountTransJnl.getHldExpDt());
            mainAccountHoldStore.setExpTim(accountTransJnl.getHldExpTm());
            mainAccountHoldStore.setHldBal(hldAmt.getCent());
            mainAccountHoldStore.setHldCd(accountTransJnl.getHldCd());
            mainAccountHoldStore.setHldNo(accountTransJnl.getHldNo());
            mainAccountHoldStore.setHldSts(HoldStatus.HOLD_HOLD.getCode());
            mainAccountHoldStore.setOrdNo(accountTransJnl.getOrdNo());
            mainAccountHoldStore.setOrdTyp(accountTransJnl.getOrdTyp());
            mainAccountHoldStore.setRmk(accountTransJnl.getRmk());
            mainAccountHoldStore.setUsrId(accountTransJnl.getHldUsrId());
            accountHoldMapper.insert(mainAccountHoldStore);
        }

    }

    public void updateAccountBalance(TransJnl accountTransJnl,
                                     AccountBalance mainAccountBalance) {
        logger.info("[更新主账户余额信息]");
        Money hldAmt = new Money(accountTransJnl.getHldAmt(), Money.DEFAULT_CURRENCY);
        Money curBal = new Money(mainAccountBalance.getCurActBal(), Money.DEFAULT_CURRENCY);
        Money uavaBal = new Money(mainAccountBalance.getUavaBal(), Money.DEFAULT_CURRENCY);
        Money valBal = curBal.subtract(uavaBal);

        Money holdSubBal = valBal.subtract(hldAmt);
        if (holdSubBal.compareTo(zero) < 0) {
            throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_BAL_NOMUCH, "主账户余额不足");
        }
        /** 预留透支额度检查 */
        uavaBal = uavaBal.add(hldAmt);
        Money lastBalAmt = new Money(mainAccountBalance.getLastActBal(), Money.DEFAULT_CURRENCY);
        String lastAcDate = mainAccountBalance.getUpdDat();
        Money lastUavaBal = new Money(mainAccountBalance.getLastUavaBal(), Money.DEFAULT_CURRENCY);
        String actPostDt = accountTransJnl.getActDat();
        if (Long.valueOf(actPostDt) > Long.valueOf(lastAcDate)) {
            lastBalAmt = new Money(mainAccountBalance.getCurActBal(), Money.DEFAULT_CURRENCY);
            lastUavaBal = new Money(mainAccountBalance.getUavaBal(), Money.DEFAULT_CURRENCY);
            lastAcDate = actPostDt;
        }
        mainAccountBalance.setLastActBal(lastBalAmt.getCent());
        mainAccountBalance.setLastUavaBal(lastUavaBal.getCent());
        mainAccountBalance.setUavaBal(uavaBal.getCent());
        mainAccountBalance.setActNo(mainAccountBalance.getActNo());
        mainAccountBalance.setUpdDat(lastAcDate);
        mainAccountBalance.setLastHldNo(accountTransJnl.getHldNo());
        mainAccountBalance.setLastHldJrn(accountTransJnl.getJrnNo());
        int retCd = accountBalanceMapper.updateBalByActNo(mainAccountBalance);
        if (retCd == 0) {
            throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_UPDBAL_FAIL,
                    "更新主账户余额失败");
        }
    }

    public void writeMainActDrCrDetail(TransJnl transJnl,
                                       AccountBalance mainAccountBalance) {
        logger.info("[Class of HoldAccount writ debit or credit detail!]");
        String vchNo = (StringUtils.isBlank(transJnl.getVchNo())) ? transJnl
                .getJrnNo() : transJnl.getVchNo();

        AccountDetail mainAccountDetail = new AccountDetail();
        long lastActBal = mainAccountBalance.getLastActBal();
        long lastUavaBal = mainAccountBalance.getLastUavaBal();
        Money objLastActBal = new Money(lastActBal, Money.DEFAULT_CURRENCY);
        Money objLastUavaBal = new Money(lastUavaBal, Money.DEFAULT_CURRENCY);
        Money objLastAvaBal = objLastActBal.subtract(objLastUavaBal);
        mainAccountDetail.setSysDat(CoreDateUtils.yyyymmdd(new Date()));
        mainAccountDetail.setSysTim(CoreDateUtils.hh24miss(new Date()));
        mainAccountDetail.setJrnNo(transJnl.getJrnNo());
        mainAccountDetail.setJrnSeq(transJnl.getJrnSeq());
        mainAccountDetail.setActNo(mainAccountBalance.getActNo());
        mainAccountDetail.setActTyp(mainAccountBalance.getActTyp());
        mainAccountDetail.setOldTxDt(transJnl.getOldTxDt());
        mainAccountDetail.setOldTxTm(transJnl.getOldTxTm());
        mainAccountDetail.setActDat(transJnl.getActDat());
        mainAccountDetail.setTxCd(transJnl.getHldTxCd());
        mainAccountDetail.setBusTyp(transJnl.getHldBusTyp());
        mainAccountDetail.setDcFlg(DebitCreditHoldFlag.HOLD_FLAG.getCode());
        mainAccountDetail.setTxDesc(transJnl.getTxDesc());
        mainAccountDetail.setOrdTyp(transJnl.getOrdTyp());
        mainAccountDetail.setOrdNo(transJnl.getOrdNo());
        mainAccountDetail.setUsrId(transJnl.getCrUsrId());
        mainAccountDetail.setTxAmt(transJnl.getOrdAmt());
        mainAccountDetail.setFeeAmt(transJnl.getFeeAmt());
        mainAccountDetail.setMercId(transJnl.getMercId());
        mainAccountDetail.setVchNoTyp(transJnl.getVchNoTyp());
        mainAccountDetail.setVchNo(transJnl.getVchNo());
        mainAccountDetail.setRmk(transJnl.getTxDesc());
        mainAccountDetail.setBusCnl(transJnl.getBusCnl());
        mainAccountDetail.setSysCnl(transJnl.getSysCnl());
        mainAccountDetail.setHldAmt(transJnl.getHldAmt());
        mainAccountDetail.setHldNo(transJnl.getHldNo());
        mainAccountDetail.setBal(mainAccountBalance.getCurActBal());
        mainAccountDetail.setNotTxAmt(mainAccountBalance.getNotTxAvaBal());
        mainAccountDetail.setUavaBal(mainAccountBalance.getUavaBal());
        mainAccountDetail.setLastAvaBal(objLastAvaBal.getCent());
        mainAccountDetail.setMainCdSts(CapStsFlg.CAP_STATUS_NORMAL.getCode());
        mainAccountDetail.setUpdBalFlg(UpdBalFlg.IS_UPDATE.getCode());
        mainAccountDetail.setBal(mainAccountBalance.getCurActBal());

        if (StringUtils.isBlank(transJnl.getVchNoTyp()))
            mainAccountDetail.setVchNoTyp(VchNoType.VOUCHER_TYPE_JRN.getCode());
        else
            mainAccountDetail.setVchNoTyp(transJnl.getVchNoTyp());
        mainAccountDetail.setVchNo(vchNo);
        accountDetailMapper.insert(mainAccountDetail);
    }

}
